home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
Apps
/
ScreenSavers
/
BackSpaceViews
/
StarShipView.BackModule
/
NewSpaceView.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
15KB
|
685 lines
// NewSpaceView.m
//
// This class implements the flying starfield screen saver view.
// This is mostly the code from SpaceView in BackSpace
//
// Hacked by R.S. Brown for added functionality
#import "NewSpaceView.h"
#import "Thinker.h"
#import "psfuncts.h"
#import "spaceFuncts.h"
#import <dpsclient/wraps.h>
#import <appkit/NXImage.h>
#import <objc/zone.h>
#import <mach/mach.h>
#import <c.h>
#import <libc.h>
#import <math.h>
#import "StarShipView.h"
#define PI (3.141592653589)
#define SCREENMOVE 1000 //how often screen moves
#define LIGHTMOVE 10 //how often lights move
@implementation NewSpaceView
+ initialize
{
if ( self == [NewSpaceView class] )
{
// load PostScript procedures SpaceView needs
// must be careful these are loaded into the correct context
loadPSProcedures();
}
return self;
}
//takes theta and distance and stuffs it into x &y for *p
- convertToXY:(STAR *)p
{
p->draw->x = floor(bounds.size.width / 2 + (p->distance * cos(p-> theta)));
p->draw->y = floor(bounds.size.height / 2 + (p->distance * sin(p-> theta)));
return self;
}
- oneStep
{
int i, stopTest, count;
int starsInBArray = 0;
int starsInWArray = 0;
STAR *p;
NXPoint *t;
float adjustedStarSpeed;
STAR test;
NXPoint testP;
if (nstars < NSTARS && !hidden) [self addStar];
stopTest = 0;
for (i=0; i<nstars; i++)
{
p = &stars[i];
p->distance += (p->delta);
if(!stopping)
adjustedStarSpeed = starSpeed;
else
adjustedStarSpeed = 1.0;
p->delta = p->delta * p->ddelta * stopDelta * adjustedStarSpeed;
if(p->delta < .01){ // this must be less than starting p>delta
stopTest++;
p->delta = 0.0;
}
[self convertToXY:p];
// only draw the star if it moved > 1 pixel or stopped
if (p->draw->x != p->erase->x ||
p->draw->y != p->erase->y || stopped){
BOOL mustErase = NO;
// add star to the erasure array
b[starsInBArray] = *p->erase;
bc[starsInBArray] = p->c;
if (p->distance > p->changepoint[p->changemode]){
(p->c)++; // increment character for next star size
(p->changemode)++;
}
// clipping is off, so we must not draw outside view.
// replace stars that go too far...
if (p->draw->x < smallScreenRect.origin.x+3 ||
p->draw->y < smallScreenRect.origin.y+3 ||
p->draw->x+7 > smallScreenRect.size.width +
smallScreenRect.origin.x - 3 ||
p->draw->y+7 > smallScreenRect.size.height +
smallScreenRect.origin.y - 3){
[self replaceStarAt:i];
//printf("replaced star at %d\n",i);
mustErase = YES;
}
w[starsInWArray] = *p->draw;
wc[starsInWArray] = p->c;
if(mustErase){
testP.x = b[starsInBArray].x;
testP.y = b[starsInBArray].y;
test.erase = &testP;
if ([self allowBStars:&test])starsInBArray++;
}
else if ([self allowBStars:p])starsInBArray++;
if ([self allowWStars:p]) starsInWArray++;
t = p->draw; p->draw = p->erase; p->erase = t;
}
}
bc[starsInBArray] = wc[starsInWArray] = 0; //null terminate string
if (starsInBArray){
for (i=0; i<(starsInBArray-1); i++){
bOffsets[i].x = b[i+1].x - b[i].x;
bOffsets[i].y = b[i+1].y - b[i].y;
}
bOffsets[i].x = bOffsets[i].y = 0;
count = 0;
while (count < starsInBArray){
char tc;
int j;
// You get the best performance if you put out all the stars
// at once. This causes noticable flicker, so I put out
// 100 of the stars per iteration. This gives reasonable speed
// and flicker is hardly noticable. Besides, stars
// _should_ flicker a little...
int t = (starsInBArray - count);
i = (t < STARSPERIT)?t:STARSPERIT;
j = i + count;
PSsetgray(NX_BLACK);
tc = bc[j]; bc[j] = 0;
PSWXYShow(b[count].x, b[count].y, &bc[count],
(float *)(&bOffsets[count].x), i*2);
bc[j] = tc;
count += STARSPERIT;
}
}
if (starsInWArray){
for (i=0; i<(starsInWArray-1); i++){
wOffsets[i].x = w[i+1].x - w[i].x;
wOffsets[i].y = w[i+1].y - w[i].y;
}
wOffsets[i].x = wOffsets[i].y = 0;
count = 0;
while (count < starsInWArray){
char tc;
int j;
// You get the best performance if you put out all the stars
// at once. This causes noticable flicker, so I put out
// 100 of the stars per iteration. This gives reasonable speed
// and flicker is hardly noticable. Besides, stars
// _should_ flicker a little...
int t = (starsInWArray - count);
i = (t < STARSPERIT)?t:STARSPERIT;
j = i + count;
PSsetgray(NX_WHITE);
tc = wc[j]; wc[j] = 0;
PSWXYShow(w[count].x, w[count].y, &wc[count],
(float *)(&wOffsets[count].x), i*2);
wc[j] = tc;
count += STARSPERIT;
}
}
if((stopTest >= (nstars - 1)) && stopping && !stopped){
stopped = YES;
[bodyController starsStopped];
}
screenChangeTime++;
if(screenChangeTime > SCREENMOVE){
[self drawScreen];
[starShip viewScreenResized:&smallScreenRect];
}
lightChangeTime++;
if(lightChangeTime > LIGHTMOVE){
lightChangeTime = 0;
[self drawScreenLights];
}
return self;
}
// make the actual image 8 pixels bigger in each direction
// then make the avoidance rect -8 pixels to width and height of image
//for example
// if the image you want to avoid is 5 pixels square
// make image 21 X 21 with a black background and
//center the actual image. make the avoidance rect
//0,0,13,13
//The reason for this is that the stars are a max of
//7 X 7 pixels and drawn from lower left corner
- (BOOL) allowWStars:(const STAR *)p
{
// avoidance rectangles are set by message from body objects
int ii;
NXRect avoid;
BOOL allow;
allow = YES;
// return yes if point is outside of all avoidance rectangles
for(ii=0;ii < [avoidStorage count];ii++){
avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid;
// just return if voidRect not set
if ((!avoid.size.width) ||
p->draw->x < avoid.origin.x ||
p->draw->y < avoid.origin.y ||
p->draw->x > avoid.origin.x+avoid.size.width ||
p->draw->y > avoid.origin.y+avoid.size.height){
}
else
allow = NO;
}
return allow;
}
//check the ones that need erasing
//returns yes if outside avoidance rect
- (BOOL) allowBStars:(const STAR *)p
{
int ii;
NXRect avoid;
BOOL allow;
allow = YES;
// return yes if point is outside of all avoidance rectangles
for(ii=0;ii < [avoidStorage count];ii++){
avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid;
// just return if voidRect not set
if ((!avoid.size.width) ||
p->erase->x < avoid.origin.x ||
p->erase->y < avoid.origin.y ||
p->erase->x > avoid.origin.x+avoid.size.width ||
p->erase->y > avoid.origin.y+avoid.size.height){
}
else
allow = NO;
}
return allow;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
// these were in original code - I don't know if they do anything
//[self allocateGState]; // For faster lock/unlockFocus
//[self setClipping:NO]; // even faster...
loadPSProcedures();
loadSpaceProcedures();
PSWDefineFont("StarFont");
PSselectfont("StarFont", 1.0);
// not really needed
// for completeness - view gets resized right away by sizeTo method
[self setSmallScreenRect:frameRect];
[self setRadius];
screenChangeTime = 0;
screenResizeLimit = 0;
screenResizeDir = 1;
lightChangeTime = 0;
lightLimit = 16;
stopDelta = 1.0;
stopped = NO;
stopping = NO;
hidden = NO;
lightInc = floor(smallScreenRect.size.width/60);
lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
smallScreenRect.origin.x;
lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
smallScreenRect.origin.x;
lights.y_pos = floor(smallScreenRect.origin.y/2);
[self setStarsStopped]; //set stars stopped at the beginning
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
// this drawself doesn't really draw the view at all.
// in fact it just promotes the window to screen depth...
NXRect t = {0,0,1,1};
PSsetrgbcolor(.333,.222,.111);
NXRectFill(&t); //yucky trick for window depth promotion!
PSsetgray(NX_BLACK); NXRectFill(&t);
PSselectfont("StarFont", 1.0);
return self;
}
- setSmallScreenRect:(const NXRect *)newRect;
{
//sets variable to bounds of the small window in the viewscreen rect
smallScreenRect.size.width = newRect->size.width;
smallScreenRect.size.height = newRect->size.height;
smallScreenRect.origin.x = newRect->origin.x;
smallScreenRect.origin.y = newRect->origin.y;
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
NXSize delta;
[super sizeTo:width :height];
delta.width = floor(width * .15);
delta.height = floor(height * .15);
smallScreenRect.size.width = width - delta.width;
smallScreenRect.size.height = height - delta.height;
smallScreenRect.origin.x = (delta.width / 2);
smallScreenRect.origin.y = (delta.height / 2);
screenChangeTime = SCREENMOVE; //put them on screen
lightChangeTime = LIGHTMOVE;
lightLimit = 16;
lightInc = floor(smallScreenRect.size.width/60);
lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
smallScreenRect.origin.x;
lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
smallScreenRect.origin.x;
lights.y_pos = floor(smallScreenRect.origin.y/2);
if (oldSize.width != bounds.size.width ||
oldSize.height != bounds.size.height)
{
oldSize.width = bounds.size.width;
oldSize.height = bounds.size.height;
nstars = 0;
[self setRadius];
[self display];
[starShip viewScreenResized:&smallScreenRect];
}
return self;
}
// only call addStar if there is room in the stars array!
- addStar
{
[self replaceStarAt:nstars++];
return self;
}
- replaceStarAt:(int)index
{
float dist, t;
int tries = 0;
STAR *p = &stars[index];
BOOL inBounds;
p->draw = &p->r1;
p->erase = &p->r2;
do {
p->theta = randBetween(0,(2*PI));
if (tries++ < 3) p->distance = randBetween(1, radius);
else p->distance = randBetween(1, p->distance);
inBounds = YES;
[self convertToXY:p];
if (p->draw->x < smallScreenRect.origin.x+3 ||
p->draw->y < smallScreenRect.origin.y+3 ||
p->draw->x + 7 > smallScreenRect.size.width +
smallScreenRect.origin.x - 3 ||
p->draw->y + 7 > smallScreenRect.size.height +
smallScreenRect.origin.y - 3)
{
inBounds = NO;
}
} while (!inBounds);
//take out
if(stopping){
p->delta = 0.0;
p->ddelta = 1.0;
}
else{
p->delta = .2;
//p->ddelta = randBetween(1.0, 1.1);
p->ddelta = randBetween(1.0, 1.05);
}
t = randBetween(0, (0.42*radius));
dist = MAX(20,t);
p->changepoint[0] = p->distance + 5; // to b
p->changepoint[1] = p->changepoint[0] - 5 + dist + dist; // to c
p->changepoint[2] = p->changepoint[1] + dist; // to d
p->changepoint[3] = p->changepoint[2] + dist; // to e
p->changepoint[4] = p->changepoint[3] + dist; // to f
p->changepoint[5] = 100000; // never change to g
p->changemode = 0;
if ((++toggle) & 1) p->c = 'a';
else p->c = 'g';
p->r2 = p->r1;
return self;
}
- drawScreen
{
screenChangeTime = 0;
PSscreenBorder(smallScreenRect.origin.x,
smallScreenRect.origin.y,
smallScreenRect.size.width + smallScreenRect.origin.x,
smallScreenRect.size.height + smallScreenRect.origin.y,
smallScreenRect.origin.y-2.5,5.0,0.0);
if(screenResizeDir == 1){
smallScreenRect.size.width++;
smallScreenRect.size.height++;
smallScreenRect.origin.x--;
smallScreenRect.origin.y--;
}
else{
smallScreenRect.size.width--;
smallScreenRect.size.height--;
smallScreenRect.origin.x++;
smallScreenRect.origin.y++;
}
PSscreenBorder(smallScreenRect.origin.x,
smallScreenRect.origin.y,
smallScreenRect.size.width + smallScreenRect.origin.x,
smallScreenRect.size.height + smallScreenRect.origin.y,
smallScreenRect.origin.y-2.5,5.0,1.0);
screenResizeLimit++;
if(screenResizeLimit > 7){
if(screenResizeDir)
screenResizeDir = 0;
else
screenResizeDir = 1; //reverse the values
screenResizeLimit = 0;
}
return self;
}
- drawScreenLights
{
float red,green,blue;
PSscreenLights(lights.l_x_pos,lights.r_x_pos,
lights.y_pos,0.0,0.0,0.0);
lights.l_x_pos -= lightInc;
lights.r_x_pos += lightInc;
lightLimit++;
if(lightLimit > 15){
lightLimit = 0;
lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
smallScreenRect.origin.x;
lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
smallScreenRect.origin.x;
lights.y_pos = smallScreenRect.origin.y/2;
}
red = randBetween(0.1,1.0); //make sure never black
green = randBetween(0.0,1.0);
blue = randBetween(0.0,1.0);
PSscreenLights(lights.l_x_pos,lights.r_x_pos,
lights.y_pos,red,green,blue);
return self;
}
//this causes stars to slow down and stop - continue being displayed
- stopStars
{
// needs to be < 1 so that the stars stop eventually
// this will control rate of stoppage
stopDelta = 0.90;
stopping = YES;
return self;
}
// causes stars to disappear altogether -
//only works if stars are stopped
//send a setStarsStopped message first
- hideStars
{
[self setStarsStopped];
PSsetgray(0);
NXRectFill(&smallScreenRect);
nstars = 0;
hidden = YES;
return self;
}
- startStars
{
STAR *p;
int ii;
hidden = NO;
for (ii=0; ii < NSTARS; ii++){
p = &stars[ii];
p->delta = 0.2;
}
stopDelta = 1.0;
stopping = NO;
stopped = NO;
return self;
}
- (BOOL)isStopped
{
return stopped;
}
- (BOOL)isStopping
{
return stopping;
}
- setRadius
{
float x = smallScreenRect.size.width;
float y = smallScreenRect.size.height;
radius = (sqrt(x*x + y*y))/2;
return self;
}
- (const char *)windowTitle
{
return "The Final Frontier";
}
- setAvoidRect:(Storage *)storage
{
avoidStorage = storage;
return self;
}
- didLockFocus
{
PSselectfont("StarFont", 1.0);
return self;
}
- (BOOL)useBufferedWindow
{ return NO;
}
- inspector:sender
{
return [sender spaceInspector];
}
- (BOOL)ignoreMouseMovement
{ return NO;
}
- inspectorWillBeRemoved
{ return self; // just a prototype
}
- inspectorInstalled
{ return self; // just a prototype
}
- setStarShipOutlet:(id)outlet
{
starShip = outlet;
return self;
}
- setBodyControllerOutlet:(id)outlet
{
bodyController = outlet;
return self;
}
- setStarSpeed:sender
{
starSpeed = [sender floatValue];
return self;
}
//this sets stars instantly stopped
- setStarsStopped
{
STAR *p;
int ii;
nstars = 0;
for(ii=0;ii < NSTARS;ii++){
[self addStar];
p = &stars[ii];
p->delta = 0.0;
}
stopping = YES;
stopped = YES;
return self;
}
@end
@implementation View(nonretainedFillMethod)
// I add this method as a category of View to be sure that all
// my views implement it. I really want to use nonretained windows
// but they are drawn via drawSelf at all kinds of goofy times. It
// seems like the kit kind of throws up its hands when it doesn't have
// a buffer to draw from, so you get a lot more drawSelfs than you need,
// and sometimes you don't get them when you really want them. I know
// when I need the background filled in black, so I factor that out of
// my drawSelf and then drawself only draws things that are already on
// screen so you don't see it happen. I will only call this method on
// a nonretained (and full screen) window.
- fillBoundsWithBlack
{
if ([self canDraw])
{
[self lockFocus];
PSsetgray(NX_BLACK);
NXRectFill(&bounds);
[self unlockFocus];
}
return self;
}
@end